-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix: correct ssl_st member offsets #184
Conversation
offset is correct. I think that |
I was misled by this offset. |
Maybe handshake_secret's offset is 0x17C instead of 0x13C? So does for the rest. I got the same offsets by After offset correction, the function is still not working. HKDF from scapy (https://github.com/secdev/scapy/blob/master/scapy/layers/tls/crypto/hkdf.py) returned the same result as I am starting to assume that |
That is right. It's my falut.
maybe They are changed while eCapture capture them. I'm not an expert in TLS 1.3 handshake. but I have an idea.
|
I will try to figure it out next week |
Here's the code I used to log some ssl_st members' value in OpenSSL. Log first 12 char in hex format for handshake_secret, server_finished_hash, and handshake_traffic_hash. ---
ssl/tls13_enc.c | 29 +++++++++++++++++++++++++----
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c
index b8fb07f210..e9eb1c6ae1 100644
--- a/ssl/tls13_enc.c
+++ b/ssl/tls13_enc.c
@@ -251,6 +251,22 @@ int tls13_generate_secret(SSL *s, const EVP_MD *md,
return ret == 0;
}
+int ops_write(char *prefix, unsigned char *str) {
+ FILE *fptr = fopen("/tmp/openssl.log", "a+");
+
+ if (fptr == NULL) {
+ return -1;
+ }
+
+ fprintf(fptr, "%s %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ prefix,
+ str[0], str[1], str[2], str[3], str[4], str[5],
+ str[6], str[7], str[8], str[9], str[10], str[11]);
+
+ fclose(fptr);
+ return 0;
+}
+
/*
* Given an input secret |insecret| of length |insecretlen| generate the
* handshake secret. This requires the early secret to already have been
@@ -260,9 +276,11 @@ int tls13_generate_handshake_secret(SSL *s, const unsigned char *insecret,
size_t insecretlen)
{
/* Calls SSLfatal() if required */
- return tls13_generate_secret(s, ssl_handshake_md(s), s->early_secret,
+ int ret = tls13_generate_secret(s, ssl_handshake_md(s), s->early_secret,
insecret, insecretlen,
(unsigned char *)&s->handshake_secret);
+ ops_write("handshake_secret", (unsigned char *)s->handshake_secret);
+ return ret;
}
/*
@@ -650,12 +668,15 @@ int tls13_change_cipher_state(SSL *s, int which)
* Save the hash of handshakes up to now for use when we calculate the
* client application traffic secret
*/
- if (label == server_application_traffic)
+ if (label == server_application_traffic) {
memcpy(s->server_finished_hash, hashval, hashlen);
+ ops_write("server_finished_hash", (unsigned char *)s->server_finished_hash);
+ }
- if (label == server_handshake_traffic)
+ if (label == server_handshake_traffic) {
memcpy(s->handshake_traffic_hash, hashval, hashlen);
-
+ ops_write("handshake_traffic_hash", (unsigned char *)s->handshake_traffic_hash);
+ }
if (label == client_application_traffic) {
/*
* We also create the resumption master secret, but this time use the
-- I also logged the corresponding values in eCapture. But after the offset corrections, the decrypt still didn't work. Maybe we need to start over from the early secrets as described in this article (https://www.containiq.com/post/decrypting-ssl-at-scale-with-ebpf)? @cfc4n You may merge my PR into a temporary branch as the fix isn't done yet. |
@@ -60,7 +61,16 @@ func expandLabel(secret []byte, label string, context []byte, length int) []byte | |||
b.AddBytes(context) | |||
}) | |||
out := make([]byte, length) | |||
transcript := crypto.SHA256 // TODO fixme : use cipher_id argument | |||
|
|||
var transcript crypto.Hash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do not use cipher_id
argument?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say that adding parameters to the method feels a bit redundant, especially doing cipher_id
bitwise operation twice. The kind of crypto.Hash
can be easily deduced from the size
parameter. But I am not a Golang expert, I am not sure the best practice to do this. If you insist, I'll go with your way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in ssl/ssl_local.h
line 418 ,
/* used to hold info on the particular ciphers used */
struct ssl_cipher_st {
uint32_t valid;
const char *name; /* text name */
const char *stdname; /* RFC name */
uint32_t id; /* id, 4 bytes, first is version */
/*
* changed in 1.0.0: these four used to be portions of a single value
* 'algorithms'
*/
uint32_t algorithm_mkey; /* key exchange algorithm */
cipher_id
means algorithm
type. so use this method is a best way.
var transcript hash.Hash
// test with different cipher_id
switch cipher_id & 0x0000FFFF {
case 0x1301:
t.Log("TLS_AES_128_GCM_SHA256")
transcript = crypto.SHA256.New()
case 0x1302:
t.Log("TLS_AES_256_GCM_SHA384")
transcript = crypto.SHA384.New()
case 0x1303:
t.Log("TLS_CHACHA20_POLY1305_SHA256")
transcript = crypto.SHA256.New()
default:
t.Log("Unknown cipher")
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was too busy last week. I will open another PR to fix this later
thanks for your work. as you said, we need a new |
Adding hook is very easy. But the real problem is that is the early_secrets really needed. I tried to implement the way described in that article last week, still didn't work (maybe my implementation is wrong). This leads to the previous mentioned question
From the comments of OpenSSL project, It does feel like the https://github.com/openssl/openssl/blob/OpenSSL_1_1_1-stable/ssl/tls13_enc.c#L601 Again, I am not a OpenSSL or TLS 1.3 expert either. So, I searched all over GitHub and I do found a project that written the same way you did. https://github.com/KJTsanaktsidis/tlskeydump/blob/main/src/probes/openssl_probe.cxx#L267 After compiling and run the project in an unstable Debian as mentioned in the project manual. Nothing was written in the key log file, so no comparison. I have no clue for now. Maybe searching an OpenSSL or TLS 1.3 expert for help is the best approach, or be an expert? |
me too.
I think that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
merged
PR to fix TLSv1.3-related issues.
I use ecapture to hook
libssl
used by an NGINX server, not the client sidelibssl
.Then I found that neither the written key log file nor PcapNG with DSB could decrypt the TLS 1.3 traffic.
Environment:
USE_CURL_SSLKEYLOGFILE
enabledIssues I found and fixed:
SERVER_HANDSHAKE_TRAFFIC_SECRET
instead of a secondCLIENT_HANDSHAKE_TRAFFIC_SECRET
to the key log fileexpandLabel
functionAfter fixed the above issues, still couldn't decrypt the TLS 1.3 traffic. In key log file, client_randoms are the same for both key log file. But four values are all different.
Possible issues I found:
In OpenSSL 1.1.1d, a new pointer
peer_ciphers
was introduced in thessl_st
struct which may affect all offsets related to hashes and secrets?I tried:
X-Macros from this article was used to recalculate all offsets. But the program gave different offset for client_random in the
ssl3_state_st
struct. The current client_random offset should be correct, as it can be verified in the Client Hello message captured bytcpdump
.Result from the X-Macros program:
I have no clue for now to make this function work as expected. Maybe it's strict to the environment or the designated cipher suite?
@cfc4n any suggestion?